﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace SmartMathLibrary.NonlinearEquationSolvers
{
    /// <summary>
    /// Find one root of the multi variant real function by using the Newton-Raphson method.
    /// </summary>
    [Serializable]
    public class MultivariantNewtonRaphsonRootFinder : AbstractRootFinder
    {
        /// <summary>
        /// The multivariate real function for finding the roots.
        /// </summary>
        private HardMultivariateRealFunction function;

        /// <summary>
        /// Initializes a new instance of the <see cref="MultivariantNewtonRaphsonRootFinder"/> class.
        /// </summary>
        /// <param name="function">The multivariate real function for finding the roots.</param>
        public MultivariantNewtonRaphsonRootFinder(HardMultivariateRealFunctionPointer function)
        {
            this.function = new HardMultivariateRealFunction(function);
        }

        /// <summary>
        /// Gets or sets the multivariate real function for finding the roots.
        /// </summary>
        /// <value>The multivariate real function for finding the roots.</value>
        public HardMultivariateRealFunction Function
        {
            get { return function; }
            set { function = value; }
        }

        /// <summary>
        /// Find one root of the multi variant real function by using the Newton-Raphson method. The x 
        /// value has to be choose useful to find a root.
        /// </summary>
        /// <param name="x">The start value of the approximation.</param>
        /// <param name="precision">The precision of the result.</param>
        /// <param name="iterations">The number of iterations to find a root.</param>
        /// <returns>One root of the multi variant real function.</returns>
        public GeneralVector FindRoots(GeneralVector x, double precision, int iterations)
        {
            RealPartialDerivativeApproximator approximator = new RealPartialDerivativeApproximator(this.Function);

            for (int i = 0; i < iterations; i++)
            {
                double functionResult = this.function.SolveAt(x);
                GeneralVector parameterVector = approximator.LimitingValueApproximation(x);

                if (Math.Abs(functionResult) < precision)
                {
                    this.NeededIterations = i;
                    this.PrecisionError = false;
                    this.RelativeError = Math.Abs(functionResult);

                    return x;
                }

                for (int j = 0; j < parameterVector.Count; j++)
                {
                    x[j] -= (this.function.SolveAt(x) / parameterVector[j]);
                }
            }

            this.PrecisionError = true;
            this.NeededIterations = iterations;
            this.RelativeError = Math.Abs(Function.SolveAt(x));

            return x;
        }
    }
}